#include "device_mgr.h"

#include <stdlib.h>
#include <string.h>

#include "../../mk_lib/mk_logger.h"
#include "../../devmgr/os_selector.h"
#include "../../os/os_thread.h"

extern "C" int get_max_io_delay();

device_mgr::device_mgr(void)
{
	init();
}

device_mgr::~device_mgr(void)
{
}

int device_mgr::init()
{
	_dev_discover_ptr = os_selector::get_device_discover();

	return 0;
}

device_base * device_mgr::get_dev_from_handle( HANDLE dev_handle )
{
	MK_POSITION pos;
	device_base * dev = _dev_discover_ptr->get_first_device(pos);

	while(dev)
	{
		HANDLE devHandle = (HANDLE)dev;
		if(dev_handle == devHandle)
		{
			return dev;
		}

		dev = _dev_discover_ptr->get_next_device(pos);
	}

	return NULL;

}

MK_POSITION device_mgr::get_pos_from_handle( HANDLE dev_handle )
{

	MK_POSITION pos;
	device_base * dev = _dev_discover_ptr->get_first_device(pos);

	while(dev)
	{
		HANDLE devHandle = (HANDLE)dev;
		if(dev_handle == devHandle)
		{
			return pos;
		}

		dev = _dev_discover_ptr->get_next_device(pos);
	}

	return NULL;
}


int device_mgr::get_dev_num()
{
	return _dev_discover_ptr->get_device_number();
}

int device_mgr::enum_device(char * parameter)
{
	if(!_dev_discover_ptr) 
		init();

	int num = _dev_discover_ptr->enum_and_create_devices(parameter);

	return num;
}


HANDLE device_mgr::add_exist_device( char * name, HANDLE hCard )
{
	device_base * dev = _dev_discover_ptr->add_new_device(name, hCard);
	return (HANDLE)dev;
}

HANDLE device_mgr::get_first_device()
{
	device_base * dev = _dev_discover_ptr->get_first_device(_curr_pos);
	
	return (HANDLE)dev;
}



HANDLE device_mgr::get_next_device()
{
	device_base * dev = _dev_discover_ptr->get_next_device(_curr_pos);

	return (HANDLE)dev;
}


HANDLE device_mgr::get_current_device()
{
	device_base * dev = _dev_discover_ptr->get_device_by_position(_curr_pos);

	return (HANDLE)dev;
}


int device_mgr::get_dev_handle_by_name( char * name, HANDLE * devHandle )
{
	device_base * dev = get_dev_by_name(name);
	if(!dev)
	{
		return 1;
	}

	* devHandle = (HANDLE)dev;
	return 0;
}




int device_mgr::open_device(HANDLE dev_handle)
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(!dev)
	{
		return 1;
	}

	if( 0 != dev->open())
	{
		return 2;
	}
	
	return 0;
}

int device_mgr::open_device_by_name( char * name, HANDLE * dev_handle )
{
	device_base * dev = get_dev_by_name(name);
	if(!dev)
	{
		return 1;
	}

	if(0 != dev->open())
	{
		return 2;
	}
	
	* dev_handle = (HANDLE)dev;
	return 0;
}

int device_mgr::close_device( HANDLE dev_handle )
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(!dev)
	{
		return 1;
	}

	return dev->close();
}

int device_mgr::transmit_apdu(HANDLE dev_handle, apdu * pApdu, int * sw )
{
	long nRet = 0;
	int buff_size = 0;
	unsigned char recBuff[2048] = {0};
	int rec_len = 2048;
	device_base * dev = NULL;
	int retry = 3;

	int ioDelay = get_max_io_delay();

	if( 0 != pApdu->serialize(NULL, &buff_size))
	{
		*sw = MCOS_SW_COMM_SERIALIZE;
		return 1;
	}

	unsigned char * pBuff = (unsigned char*)malloc(buff_size);
	if( 0 != pApdu->serialize(pBuff, &buff_size))
	{
		*sw = MCOS_SW_COMM_SERIALIZE;
		nRet = 2;
		goto END_OF_FUN;
	}

	//retry if there is communication error
	while(retry-- > 0)
	{
		int timeOut = pApdu->get_comm_timeout();
		if(0 == device_io(dev_handle, pBuff, buff_size, recBuff, &rec_len, sw, timeOut))
		{
			break;
		}
		
		if(0 != device_present(dev_handle))
		{
			*sw = MCOS_SW_COMM_SEND;
			nRet = 3;
			goto END_OF_FUN;
		}
		
		thread_sleep(200);
	}

	if(*sw != 0x9000)
	{
		nRet = 7;
		goto END_OF_FUN;
	}

	if( 0 != pApdu->deserialize(recBuff, rec_len))
	{
		* sw = MCOS_SW_COMM_DESERILIZE;
		nRet = 6;
		goto END_OF_FUN;
	}

	* sw = pApdu->get_sw();

END_OF_FUN:
	if(pBuff) free(pBuff);

	return nRet;
}

int device_mgr::transmit_command( HANDLE dev_handle, unsigned char * cmd, int cmd_len, 
									   unsigned char * resp, int * resp_len )
{
	DBG_LOGBUFFER(cmd, cmd_len, "[raw data]send");

	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	if(0 != dev->send_data(cmd, cmd_len))
	{
		return 2;
	}

	if( 0 != dev->receive_data(resp, resp_len))
	{
		return 3;
	}

	DBG_LOGBUFFER(cmd, cmd_len, "[raw data]receive");
	return 0;
}

int device_mgr::send_raw_data( HANDLE dev_handle, unsigned char * cdb, int cdb_len, unsigned char * cmd, long cmd_len )
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	return dev->send_raw_data(cdb, cdb_len, cmd, cmd_len);
}

int device_mgr::receive_raw_data( HANDLE dev_handle, unsigned char * cdb, int cdb_len, unsigned char * resp, long *resp_len )
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	if(0 != dev->receive_raw_data(cdb, cdb_len, resp, resp_len))
	{
		return 2;
	}

	//DBG_LOGBUFFER(resp, *resp_len, "[raw data]receive");
	return 0;
}


int device_mgr::update_devices_state( int * dev_num, int * remove_num, int * new_num , char * parameter)
{
	* dev_num = _dev_discover_ptr->update_devices(remove_num, new_num, parameter);

	return 0;
}

int device_mgr::get_dev_state( HANDLE dev_handle, enmDevState * state)
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	* state = dev->get_dev_state();
	return 0;
}

int device_mgr::set_dev_state( HANDLE dev_handle, enmDevState state )
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	dev->set_dev_state(state);
	return 0;
}

int device_mgr::destroy_removed_devices()
{
	return _dev_discover_ptr->destroy_removed_deviced();
}

int device_mgr::delete_device( HANDLE dev_handle )
{	
	MK_POSITION pos = get_pos_from_handle(dev_handle);
	if(pos == 0)
	{
		return 1;
	}
	
	//move to next pos
	if(pos == _curr_pos)
	{
		get_next_device();
	}

	return _dev_discover_ptr->delete_device(pos);
}

char * device_mgr::get_dev_name( HANDLE dev_handle )
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return NULL;
	}

	return dev->get_dev_name();
}


char * device_mgr::get_dev_path(HANDLE dev_handle)
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return NULL;
	}

	return dev->get_dev_path();

}


int device_mgr::set_dev_name( HANDLE dev_handle, char * name )
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	dev->set_dev_name(name);
	return 0;
}


device_base * device_mgr::get_dev_by_name( char * name )
{
	MK_POSITION pos;
	device_base * dev = _dev_discover_ptr->get_first_device(pos);

	while(dev)
	{
		if(strcmp(name, dev->get_dev_name()) == 0)
		{
			return dev;
		}

		dev = _dev_discover_ptr->get_next_device(pos);
	}

	return NULL;
}

int device_mgr::get_dev_type( HANDLE dev_handle, long * type )
{
	device_base * dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	* type = dev->get_device_type();
	return 0;
}

int device_mgr::device_io(HANDLE dev_handle, unsigned char* input, int ilen,
						  unsigned char * output, int * olen, int * sw, int timeout)
{
	long nRet = 0;
	device_base * dev = NULL;
	int ret = 0;

	int ioDelay = get_max_io_delay();

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		*sw = MCOS_SW_DEVHANDLE_INVALID;
		return 1;
	}

	dev->set_comm_timeout(timeout);
	ret = dev->device_io(input, ilen, output, olen); 
	switch(ret)
	{
	case 0:
		*sw = 0x9000;
		break;
	case 1:
		*sw = MCOS_SW_COMM_SEND;
		break;
	case 2:
		*sw = MCOS_SW_COMM_RECEIVE;
		break;
	default:
		*sw = MCOS_SW_COMM_SEND;
		break;
	}

/*	if(0 != dev->send_data(input, ilen))
	{
		* sw = MCOS_SW_COMM_SEND;
		return 2;
	}

	//for AS508, 4ms are required under USB3.0 
	if(ioDelay > 0)
	{
		thread_sleep(ioDelay);
	}

	if( 0 != dev->receive_data(output, olen))
	{
		* sw = MCOS_SW_COMM_RECEIVE;
		return 3;
	}
	
	*sw = 0x9000;
	*/
	return 0;
}

int device_mgr::device_present( HANDLE dev_handle )
{
	long nRet = 0;
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	if(0 != dev->dev_present())
	{
		dev->close();
		dev->set_dev_state(devStateRemoved);
		_dev_discover_ptr->destroy_removed_deviced();
		return 2;
	}

	return 0;
}

int device_mgr::get_os_handle( HANDLE dev_handle, HANDLE * osHandle)
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	* osHandle = dev->get_dev_os_handle();
	
	return 0;
}

int device_mgr::lock_device( HANDLE dev_handle, long timeOut )
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	return dev->begin_transaction(timeOut);
}

int device_mgr::unlock_device( HANDLE dev_handle )
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}
	return dev->end_transaction();
}

int device_mgr::begin_transaction( HANDLE dev_handle )
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}
	return dev->begin_transaction(-1);
}

int device_mgr::end_transaction( HANDLE dev_handle )
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}
	return dev->end_transaction();
}

int device_mgr::get_hs_apdu_size( HANDLE dev_handle, int *size)
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	return dev->get_hs_apdu_size(size);
}

int device_mgr::enable_high_speed( HANDLE dev_handle, int mode )
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}
	
	return dev->enable_high_speed(mode);
}

int device_mgr::crypt_with_high_speed( HANDLE dev_handle, unsigned long algo_id, int mode,
									  int appid, int containerId, int keyId,
									  int firstPackage, unsigned char * iv, int iv_len,
									  unsigned char * input, int input_len, unsigned char * output, int *output_len )
{
	device_base * dev = NULL;

	dev = get_dev_from_handle(dev_handle);
	if(dev == NULL)
	{
		return 1;
	}

	return dev->crypt_with_high_speed(algo_id, mode, appid, containerId, keyId, 
		firstPackage, iv, iv_len, input, input_len,output, output_len);
}
